/**
 * 
 */
package cn.edu.nju.ws.sview.wyge.relations.adataset.relations.datasetRelations;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

import org.dom4j.Document;

import cn.edu.nju.ws.sview.cache.UserNameIDCache;
import cn.edu.nju.ws.sview.database.DBConnectionFactory;
import cn.edu.nju.ws.sview.wyge.relations.adataset.CustomDataset;
import cn.edu.nju.ws.sview.wyge.relations.adataset.entities.qxliu.algorithms.ClassManager;
import cn.edu.nju.ws.sview.wyge.relations.adataset.entities.qxliu.algorithms.ClassTree;
import cn.edu.nju.ws.sview.wyge.relations.adataset.relations.Pattern;
import cn.edu.nju.ws.sview.wyge.relations.adataset.relations.Relation;
import cn.edu.nju.ws.sview.wyge.relations.adataset.relations.Relations;
import cn.edu.nju.ws.sview.wyge.relations.datasets.DatasetItem;

/**
 * @author Weiyi Ge (geweiyi@gmail.com) 2012-8-12
 * 
 */
public class DatasetRelations {
	public static void main(String[] argus) {
		DatasetRelations dr = new DatasetRelations("uri", 181903600, 1,
				"geweiyi");
		System.out.println(dr.getRelations(15));
	}

	private String ds_type = null;
	private int ds_id = -1;
	private int relation_id_from = 1;
	private int step = 100;
	private int max_relation_id = 0;
	private String user;
	private String lang;//========
	private ArrayList<Integer> docs = new ArrayList<Integer>();
	private HashMap<Integer, Integer> doc_relNum = null;//=============for getRelationsByPointer
	private int total_relation_lang;
	
	private int class_num = -1;//for original
	private int prop_num = -1;//for original
	
	//===for temp Dataset
	public DatasetRelations(ArrayList<Integer> docs, int relation_id_from,
			String user, String lang) {
		this.relation_id_from = relation_id_from + 1;
		this.user = user;
		this.lang = lang;
		this.docs = docs;
		try {
			Connection conn = DBConnectionFactory.getConnection();
			PreparedStatement pstmt = conn.prepareStatement("SELECT max(relation_id) FROM relation where lang=?");
			pstmt.setString(1, lang);
			ResultSet rs = pstmt.executeQuery();
			while (rs.next()) {
				max_relation_id = rs.getInt(1);
			}
			pstmt = conn.prepareStatement("select count(relation_id) from relation where lang=?");
			pstmt.setString(1, lang);
			rs = pstmt.executeQuery();
			while (rs.next()) {
				total_relation_lang = rs.getInt(1);
			}
			rs.close();
			pstmt.close();
			conn.close();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	//=============================
	
	
	
	
	public DatasetRelations(String ds_type, int ds_id, int relation_id_from,
			String user, String lang) {
		this.ds_type = ds_type;
		this.ds_id = ds_id;
		this.relation_id_from = relation_id_from + 1;
		this.user = user;
		this.lang = lang;
		if (ds_type.equals("custom")) {
			docs = CustomDataset.getURLIDsByCustomDatasetId(ds_id);
		} else if (ds_type.equals("uri")) {
			docs = new ArrayList<Integer>();
			docs.add(ds_id);
		}
		try {
			Connection conn = DBConnectionFactory.getConnection();
			PreparedStatement pstmt = conn.prepareStatement("SELECT max(relation_id) FROM relation where lang=?");
			pstmt.setString(1, lang);
			ResultSet rs = pstmt.executeQuery();
			while (rs.next()) {
				max_relation_id = rs.getInt(1);
			}
			pstmt = conn.prepareStatement("select count(relation_id) from relation where lang=?");
			pstmt.setString(1, lang);
			rs = pstmt.executeQuery();
			while (rs.next()) {
				total_relation_lang = rs.getInt(1);
			}
			rs.close();
			pstmt.close();
			conn.close();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public DatasetRelations(String ds_type, int ds_id, int relation_id_from,
			String user) {
		this.ds_type = ds_type;
		this.ds_id = ds_id;
		this.relation_id_from = relation_id_from + 1;
		this.user = user;
		this.lang = "en";//=========
		if (ds_type.equals("custom")) {
			docs = CustomDataset.getURLIDsByCustomDatasetId(ds_id);
		} else if (ds_type.equals("uri")) {
			docs = new ArrayList<Integer>();
			docs.add(ds_id);
		}
		try {
			Connection conn = DBConnectionFactory.getConnection();
			Statement stmt = conn.createStatement();
			ResultSet rs = stmt
					.executeQuery("SELECT max(relation_id) FROM relation");
			while (rs.next()) {
				max_relation_id = rs.getInt(1);
			}
			rs.close();
			stmt.close();
			conn.close();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public boolean isItsRelationByDoc(int relation_id) {
		HashSet<String> datasetConstants = DatasetConstants.get(docs);
		HashSet<String> relationConstants = RelationConstants.get(relation_id);
		for (String s : relationConstants) {
			if (!datasetConstants.contains(s)) {
				return false;
			}
		}
		return true;
	}
	public boolean isItsRelation(int relation_id) {
		HashSet<String> datasetConstants = DatasetConstants.get(ds_type, ds_id);
		HashSet<String> relationConstants = RelationConstants.get(relation_id);
		for (String s : relationConstants) {
			if (!datasetConstants.contains(s)) {
				return false;
			}
		}
		return true;
	}
	/*public ArrayList<Relation> getOriginals(int limit, String lang){
		ArrayList<Relation> rels = new ArrayList<Relation>();
		getOriginalRelations(rels, limit, lang);
		return rels;
	}*/
	public int getAvailableRelationNum(){
		int totalNum = 0;
		ArrayList<Integer> favorites = null;
		ArrayList<Integer> trashes = null;
		String exceptStr = "";
		if(this.user != null){
			favorites = Relations.getFavoriteIds(ds_type, ds_id, user);
			trashes = Relations.getTrashIds(ds_type, ds_id, user);
			StringBuffer except = new StringBuffer(" and relation_id not in ");
			if(favorites != null && favorites.size()>0){
				if(trashes != null && trashes.size()>0){
					except.append(favorites.toString().replace("[", "(").replace("]", ","));
					except.append(trashes.toString().replace("[", "").replace("]", ")"));
				}else{
					except.append(favorites.toString().replace("[", "(").replace("]", ")"));
				}
			}else if(trashes != null && trashes.size()>0){
				except.append(trashes.toString().replace("[", "(").replace("]", ")"));
			}
			if(except.toString().contains(")")){
				exceptStr = except.toString();
			}
		}
		Connection conn = DBConnectionFactory.getConnection();
		try {
			PreparedStatement pstmt = conn.prepareStatement("select count(relation_id) from rs_ds where ds_id=? and ds_type=? and lang=? "+exceptStr);
			pstmt.setInt(1, ds_id);
			pstmt.setString(2, ds_type);
			pstmt.setString(3, lang);
			ResultSet rs = pstmt.executeQuery();
			if(rs.next()){
				totalNum = rs.getInt(1);
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		ResultSet rs = null;
		
		return totalNum;
		
	}
	public int getRelationNumForTmpDataset(int numLimit, String lang){
		int totalNum = 0;
		int start = 0;
		do{
			totalNum += getRelationNumFromPartForTmpDataset(totalNum, start, numLimit, lang);
			start += step;
		}while(totalNum<numLimit && start < totalNum);
		
		return totalNum;
	}
	public int getRelationNumFromPartForTmpDataset(int totalNum, int start, int numLimit, String lang){
		try {
			Connection conn = DBConnectionFactory.getConnection();
			PreparedStatement selps = conn
					.prepareStatement("SELECT relation_id FROM relation where lang=? order by use_times limit ?,?");
			selps.setString(1, lang);
			selps.setInt(2, start);
			selps.setInt(3, step);
			ResultSet selrs = selps.executeQuery();
			while (selrs.next()) {
				int relation_id = selrs.getInt(1);
				relation_id_from = relation_id + 1;
				if (isItsRelationByDoc(relation_id)) {//====to be completed
					totalNum++;
					if (totalNum == numLimit)
						break;
				}
			}
			selrs.close();
			selps.close();
			conn.close();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return totalNum;
	}
	public int getRelationNum(int numLimit, String lang){
		int totalNum = 0;
		int start = 0;
		do{
			totalNum += getRelationNumFromPart(totalNum, start, numLimit, lang);
			start += step;
		}while(totalNum<numLimit && start < totalNum);
		
		return totalNum;
	}
	public int getRelationNumFromPart(int totalNum, int start, int numLimit, String lang){
		try {
			Connection conn = DBConnectionFactory.getConnection();
			PreparedStatement selps = conn
					.prepareStatement("SELECT relation_id FROM relation where lang=? order by use_times limit ?,?");
			selps.setString(1, lang);
			selps.setInt(2, start);
			selps.setInt(3, step);
			ResultSet selrs = selps.executeQuery();
			while (selrs.next()) {
				int relation_id = selrs.getInt(1);
				relation_id_from = relation_id + 1;
				if (isItsRelation(relation_id) && notFavor(relation_id)
						&& notTrash(relation_id) && instanceCache(relation_id)) {
					totalNum++;
					if (totalNum == numLimit)
						break;
				}
			}
			selrs.close();
			selps.close();
			conn.close();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return totalNum;
	}
	public ArrayList<Relation> getRelationsByPointer(int start, int length,String lang){
		ArrayList<Integer> favorites = null;
		ArrayList<Integer> trashes = null;
		String exceptStr = "";
		if(this.user != null){
			favorites = Relations.getFavoriteIds(ds_type, ds_id, user);
			trashes = Relations.getTrashIds(ds_type, ds_id, user);
			StringBuffer except = new StringBuffer(" and relation_id not in ");
			if(favorites != null && favorites.size()>0){
				if(trashes != null && trashes.size()>0){
					except.append(favorites.toString().replace("[", "(").replace("]", ","));
					except.append(trashes.toString().replace("[", "").replace("]", ")"));
//					System.out.println("1:"+except.toString());
				}else{
					except.append(favorites.toString().replace("[", "(").replace("]", ")"));
//					System.out.println("2:"+except.toString());
				}
			}else if(trashes != null && trashes.size()>0){
				except.append(trashes.toString().replace("[", "(").replace("]", ")"));
//				System.out.println("3:"+except.toString());
			}

//			System.out.println("4:"+except.toString().contains(")"));
			if(except.toString().contains(")")){
				exceptStr = except.toString();
			}
//			System.out.println("ddd:"+favorites.size()+"|"+trashes.size());
		}
//		System.out.println(user+"|exceptStr|"+exceptStr.toString());
		ArrayList<Relation> rel_list = new ArrayList<Relation>();
		try {
			Connection conn = DBConnectionFactory.getConnection();
			PreparedStatement pstmt = conn.prepareStatement("select relation_id from rs_ds where ds_id=? and ds_type=? and lang=? "+exceptStr+" limit ?,?");
			ResultSet rs = null;
			Relation rel = null;
//			if(this.ds_type.contentEquals("uri")){
				pstmt.setInt(1, ds_id);
				pstmt.setString(2, ds_type);
				pstmt.setString(3, lang);
				pstmt.setInt(4, start);
				pstmt.setInt(5, length);
				rs = pstmt.executeQuery();
				while(rs.next()){
					rel = Relation.getInstance(rs.getInt(1));
					if(!rel_list.contains(rel)){
						rel_list.add(rel);
					}
				}
				rs.close();
//			}else if(this.ds_type.contentEquals("custom")){
//				if(this.doc_relNum == null){
//					countDocRelNum(conn, lang);
//				}
//				int count = 0;//==========
//				for(int doc : docs){
//					if(count<start && (count + doc_relNum.get(doc))>start){
//						pstmt.setInt(1, doc);
//						pstmt.setString(2, "uri");
//						pstmt.setString(3, lang);
//						pstmt.setInt(4, start-count);
//						pstmt.setInt(5, length);
//						rs = pstmt.executeQuery();
//						while(rs.next()){
//							rel = Relation.getInstance(rs.getInt(1));
//							if(!rel_list.contains(rel)){
//								rel_list.add(rel);
//							}
//						}
//						count += doc_relNum.get(doc);
//					}else if(count>=start){
//						pstmt.setInt(1, doc);
//						pstmt.setString(2, "uri");
//						pstmt.setString(3, lang);
//						pstmt.setInt(4, 0);
//						pstmt.setInt(5, length-rel_list.size());
//						rs = pstmt.executeQuery();
//						while(rs.next()){
//							rel = Relation.getInstance(rs.getInt(1));
//							if(!rel_list.contains(rel)){
//								rel_list.add(rel);
//							}
//						}
//						count += doc_relNum.get(doc);
//					}
//					if(rel_list.size() >= length){
//						break;
//					}
//					if(rs != null){
//						rs.close();
//					}
//				}//end of for_doc
//			}
			pstmt.close();
			conn.close();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return rel_list;
	}
//	private void countDocRelNum(Connection  conn, String lang){
//		this.doc_relNum = new HashMap<Integer, Integer>();
//		try {
//			PreparedStatement pstmt = conn.prepareStatement("select count(*) from rs_ds where ds_id=? and ds_type='uri' and lang=?");
//			for(int doc : this.docs){
//				pstmt.setInt(1, doc);
//				pstmt.setString(2, lang);
//				ResultSet rs = pstmt.executeQuery();
//				if(rs.next()){
//					doc_relNum.put(doc, rs.getInt(1));
//				}
//				rs.close();
//			}
//			pstmt.close();
//		} catch (SQLException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
//	}
	public ArrayList<Relation> getRelations(int limit, String lang) {
		ArrayList<Relation> rels = new ArrayList<Relation>();
//		System.out.println("in DatasetRelations get:"+rels.size()+"|"+limit+"|"+relation_id_from+"|"+max_relation_id);
		do {
			getCustomRelations(rels, limit, lang);
		} while (rels.size() < limit && relation_id_from < max_relation_id);
//		System.out.println("in DatasetRelations getRelations:"+rels.size()+"|"+limit+"|"+lang);
		return rels;
	}
	public ArrayList<Relation> getRelationsBackward(int limit){
		ArrayList<Relation> rels = new ArrayList<Relation>();
		int last_from = relation_id_from;
		do {
			last_from = relation_id_from;
			getCustomRelationsBackwardForTmpDataset(rels, limit);
		} while (rels.size() < limit && last_from>relation_id_from && relation_id_from>0 && relation_id_from <= max_relation_id);
		return rels;
	}
	public ArrayList<Relation> getRelations(int limit) {
		ArrayList<Relation> rels = new ArrayList<Relation>();
		int last_from = relation_id_from;
		do {
			last_from = relation_id_from;
			getCustomRelationsForTmpDataset(rels, limit);
		} while (rels.size() < limit && last_from<relation_id_from && relation_id_from < max_relation_id);
		return rels;
	}
	/*private void getOriginalRelations(ArrayList<Relation> rels, int limit, String lang) {
		System.out.println("in:"+(rels.size() < this.class_num)+"|"+rels.size()+"|"+ this.class_num);
		if(this.class_num<0 || rels.size() < this.class_num){
			HashMap<Integer, String[]> classes = new ClassManager(ds_type, ds_id).getNames();
			this.class_num = classes.size();
			ArrayList<Integer> c = new ArrayList<Integer>();
			c.addAll(classes.keySet());
//			System.out.println("call:"+c.toString());
			Collections.sort(c);
			for(int i=rels.size(); i<c.size()&&i<limit; i++){
				int cid = c.get(i);
				Pattern pattern = new Pattern();
				pattern.addTriple("v1", cid, "u"+cid);
				HashMap<String, String> variable_role = new HashMap<String, String>();
				variable_role.put("v1", "v1");
				Relation rel = new Relation(cid, classes.get(cid)[1], -1, -1, lang, null, variable_role, pattern, true);
				rels.add(rel);
			}
//			System.out.println("cids:"+c.toString());
		}
		if(rels.size()<limit && this.class_num>=0 && rels.size() > this.class_num){
			
		}
	}*/
	private void getCustomRelations(ArrayList<Relation> rels, int limit, String lang) {
		try {
			Connection conn = DBConnectionFactory.getConnection();
			PreparedStatement selps = conn
					.prepareStatement("SELECT relation_id FROM relation where relation_id>= ? and relation_id< ? and lang=?");
			int to = relation_id_from + step;
			selps.setInt(1, relation_id_from);
			selps.setInt(2, to);
			selps.setString(3, lang);
			ResultSet selrs = selps.executeQuery();
			while (selrs.next()) {
				int relation_id = selrs.getInt(1);
				relation_id_from = relation_id + 1;
				if (isItsRelation(relation_id) && notFavor(relation_id)
						&& notTrash(relation_id) && instanceCache(relation_id)) {
					Relation rel = Relation.getInstance(relation_id);
					rels.add(rel);
					if (rels.size() == limit)
						break;
				}
			}
			selrs.close();
			selps.close();
			conn.close();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	private void getCustomRelationsBackwardForTmpDataset(ArrayList<Relation> rels, int limit) {
		try {
			Connection conn = DBConnectionFactory.getConnection();
			PreparedStatement selps = conn
					.prepareStatement("SELECT relation_id FROM relation where relation_id< ? and relation_id> ? and lang=?");
			int to = relation_id_from - step;
			selps.setInt(1, relation_id_from);
			selps.setInt(2, to);
			selps.setString(3, lang);
			ResultSet selrs = selps.executeQuery();
			while (selrs.next()) {
				int relation_id = selrs.getInt(1);
				relation_id_from = relation_id;
				if (isItsRelationByDoc(relation_id)) {
					Relation rel = Relation.getInstance(relation_id);
					rels.add(rel);
					if (rels.size() == limit)
						break;
				}
			}
			selrs.close();
			selps.close();
			conn.close();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	private void getCustomRelationsForTmpDataset(ArrayList<Relation> rels, int limit) {
		try {
			Connection conn = DBConnectionFactory.getConnection();
			PreparedStatement selps = conn
					.prepareStatement("SELECT relation_id FROM relation where relation_id>= ? and relation_id< ? and lang=?");
			int to = relation_id_from + step;
			selps.setInt(1, relation_id_from);
			selps.setInt(2, to);
			selps.setString(3, lang);
			ResultSet selrs = selps.executeQuery();
			while (selrs.next()) {
				int relation_id = selrs.getInt(1);
				relation_id_from = relation_id + 1;
				if (isItsRelationByDoc(relation_id)) {//======to be completed
					Relation rel = Relation.getInstance(relation_id);
					rels.add(rel);
					if (rels.size() == limit)
						break;
				}
			}
			selrs.close();
			selps.close();
			conn.close();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	private void getCustomRelations(ArrayList<Relation> rels, int limit) {
		try {
			Connection conn = DBConnectionFactory.getConnection();
			PreparedStatement selps = conn
					.prepareStatement("SELECT relation_id FROM relation where relation_id>= ? and relation_id< ? and lang=?");
			int to = relation_id_from + step;
			selps.setInt(1, relation_id_from);
			selps.setInt(2, to);
			selps.setString(3, lang);
			ResultSet selrs = selps.executeQuery();
			while (selrs.next()) {
				int relation_id = selrs.getInt(1);
				relation_id_from = relation_id + 1;
				if (isItsRelation(relation_id) && notFavor(relation_id)
						&& notTrash(relation_id) && instanceCache(relation_id)) {
					Relation rel = Relation.getInstance(relation_id);
					rels.add(rel);
					if (rels.size() == limit)
						break;
				}
			}
			selrs.close();
			selps.close();
			conn.close();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * 如果测试结果为false，也就是确定没有实例，就返回false，否则返回true
	 */
	private boolean instanceCache(int relation_id) {
		boolean ret = true;
		try {
			Connection conn = DBConnectionFactory.getConnection();
			PreparedStatement selps = conn
					.prepareStatement("SELECT instance FROM rs_ds where relation_id = ? and ds_type = ? and ds_id = ?");
			selps.setInt(1, relation_id);
			selps.setString(2, ds_type);
			selps.setInt(3, ds_id);
			ResultSet selrs = selps.executeQuery();
			if (selrs.next()) {
				ret = selrs.getBoolean(1);
			}
			selrs.close();
			selps.close();
			conn.close();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return ret;
	}

	/**
	 * @param relation_id
	 * @return
	 */
	private boolean notFavor(int relation_id) {
		// TODO Auto-generated method stub
		if (user == null || user.equals("null")) {
			return true;
		}
		boolean ret = true;
		int user_id = 0;
		try {
			user_id = UserNameIDCache.getInstance().getUserID(user);
		} catch (Throwable e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		Connection conn = DBConnectionFactory.getConnection();
		try {
			PreparedStatement selps = conn
					.prepareStatement("SELECT * FROM re_ds_favor where relation_id = ? and user_id = ? and ds_type = ? and ds_id = ?");
			selps.setInt(1, relation_id);
			selps.setInt(2, user_id);
			selps.setString(3, ds_type);
			selps.setInt(4, ds_id);
			ResultSet selrs = selps.executeQuery();
			if (selrs.next()) {
				ret = false;
			}
			selrs.close();
			selps.close();
			conn.close();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return ret;
	}

	/**
	 * @param relation_id
	 * @return
	 */
	private boolean notTrash(int relation_id) {
		// TODO Auto-generated method stub
		if (user == null || user.equals("null")) {
			return true;
		}
		boolean ret = true;
		int user_id = 0;
		try {
			user_id = UserNameIDCache.getInstance().getUserID(user);
		} catch (Throwable e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		Connection conn = DBConnectionFactory.getConnection();
		try {
			PreparedStatement selps = conn
					.prepareStatement("SELECT * FROM re_ds_trash where relation_id = ? and user_id = ? and ds_type = ? and ds_id = ?");
			selps.setInt(1, relation_id);
			selps.setInt(2, user_id);
			selps.setString(3, ds_type);
			selps.setInt(4, ds_id);
			ResultSet selrs = selps.executeQuery();
			if (selrs.next()) {
				ret = false;
			}
			selrs.close();
			selps.close();
			conn.close();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return ret;
	}
}
